namespace Parser

open Lexer.Tocken
open ParserTable

module Parser =
    let stack = System.Collections.Stack()
    stack.Push(NonTerminal(S))

    let lookUpFromTable (nonterm: NonTerminal) (term: Terminal): seq<Symbol> =
        match parserTable.TryFind(nonterm) with
        | Some (row) ->
            match row.TryFind(term) with
            | Some (expression) -> 
                for item in Seq.rev expression do
                    stack.Push(item)
                expression
            | None -> seq { Error }
        | None -> seq { Error }

    let toStringList (s: seq<Symbol>) =
        s |> Seq.map getSymbolName |> Seq.toList

    let lookupAndToList nonterm term = 
        "[# " + (String.concat " " <| Array.toList (Array.map (fun (x:obj) -> getSymbolName (x :?> Symbol)) (stack.ToArray()))) + $" {nonterm}]" ,
        nonterm.ToString() + " ::= " + (String.concat " " (toStringList (lookUpFromTable nonterm term)))

    let rec parser (input: Tocken list) =
        if stack.Count = 0 then
            [ "End!", "" ]
        else
            match input with 
            | [] -> []
            | head::tail ->
                let top = stack.Pop() :?> Symbol
                
                // printfn "\t TOP=%s" <| top.ToString()  
                match top with
                | NonTerminal nonTerminal -> 
                    (lookupAndToList nonTerminal head.TypeName)::parser(input)
                | Terminal terminal -> 
                    ("[# " + (String.concat " " <| Array.toList (Array.map (fun (x:obj) -> getSymbolName (x :?> Symbol)) (stack.ToArray()))) + $" {terminal}]" ,
                        "reduce: " + terminal.ToString() + "  // value is " +  head.Value):: parser(tail)
                | Epsilon -> parser(input)
                | _ -> []
            
